/*******************************************************************************
* Copyright (C) 2014 Maxim Integrated Products, Inc., All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL MAXIM INTEGRATED BE LIABLE FOR ANY CLAIM, DAMAGES
* OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Except as contained in this notice, the name of Maxim Integrated
* Products, Inc. shall not be used except as stated in the Maxim Integrated
* Products, Inc. Branding Policy.
*
* The mere transfer of this software does not imply any licenses
* of trade secrets, proprietary technology, copyrights, patents,
* trademarks, maskwork rights, or any other form of intellectual
* property whatsoever. Maxim Integrated Products, Inc. retains all
* ownership rights.
*******************************************************************************
*/
/*============================================================================
 * 
 * Author(s): Maxim Integrated Products
 * Contributors:
 * Date:          
 * Purpose:       
 * Description:
 * 
 *==========================================================================*/
#include <errors.h>
#include <mml_sc.h>
#include <emv_config.h>
#include <emv_slot.h>

#define STATE_PARSE_TS							1
#define STATE_PARSE_T0							2
#define STATE_PARSE_TA							3
#define STATE_PARSE_TB							4
#define STATE_PARSE_TC							5
#define STATE_PARSE_TD							6
#define STATE_PARSE_HIST_BYTES					7
#define STATE_PARSE_TCK							8
#define STATE_PARSE_END							255

#define INTER_CHAR_DEFAUT						0
#define FI_DEFAUT								1
#define DI_DEFAUT								1
#define VPP_DEFAUT								5
#define BWI_DEFAUT								4
#define CWI_DEFAUT								0xD
#define EDC_DEFAUT								LRC
#define IFS_DEFAUT								0x20
#define WI_DEFAUT								0x0A
#define TC1_DEFAUT								0

static const unsigned char emv_Di_tab[16] = {
		1, 1, 2, 4, 8, 16, 32, 1, 12, 20, 1, 1, 1, 1, 1, 1
};

static const unsigned short emv_Fi_tab[16] = {
		372, 372, 558, 744, 1116, 1488, 1860, 372, 
		372, 512, 768, 1024, 1536, 2048, 372, 372
};
    

int emv_atr_parse( emv_slot_t *slot, emv_atr_t *info )
{
	unsigned char Yi = 0;
	unsigned char data;
	unsigned char *ptr;
	unsigned char TCK;
	unsigned char K = 0; /* Number of Historical Characters */
	int state;
	unsigned int index;
	unsigned short int guard_time;
    unsigned char *atr = slot->rx_buf.data;
    int length = slot->rx_buf.length;
    
	slot->error = N_SCS_ERR_INVALID_ATR;
	info->k = 0;
	info->conv = EMV_CONV_UNKNOWN;
	info->ifsc = IFS_DEFAUT;
	info->protocol = 0;
	info->cwi = CWI_DEFAUT;
	info->bwi = BWI_DEFAUT;
	info->n = TC1_DEFAUT;
	info->wi = WI_DEFAUT;
    info->fd = 0x11;
	info->fi = FI_DEFAUT;
	info->di = DI_DEFAUT;
	info->p = VPP_DEFAUT;
	info->emv_flag = 1;
	slot->status |= EMV_STATE_MODE_SPECIFIC;
	state = STATE_PARSE_TS;
	index = 0;
	ptr = atr;
	TCK = 0;
	
	while( ptr < (atr + length) )
	{
		data = *ptr++;
		
		if ( state != STATE_PARSE_TS )
		{
			TCK ^= data ;
		}

		switch( state )
		{
            /* TS */
			case STATE_PARSE_TS:
				if ( data == 0x3B )
				{
					info->conv = EMV_CONV_DIRECT;
				}
				else if ( data == 0x3F )
				{
					info->conv = EMV_CONV_INVERSE;
				}
				state = STATE_PARSE_T0;
				break ;
			case STATE_PARSE_T0:
				K = data & 0x0F;
				Yi = data;
				if ( data & 0x10 )
				{
					state = STATE_PARSE_TA;
				}
				else if ( data & 0x20 )
				{
					state = STATE_PARSE_TB;
				}
				else
				{
					/* TB1 absent */
					if ( slot->status & EMV_STATE_COLD_RESET )
					{
						/* Cold Reset */
						info->emv_flag = 0;
					}
					if ( data & 0x40 )
					{
						state = STATE_PARSE_TC;
					}
					else if ( data & 0x80 )
					{
						state = STATE_PARSE_TD;
					}
					else
					{
						state = STATE_PARSE_HIST_BYTES;
					}
				}
				break ;
			case STATE_PARSE_TA :
				switch( index )
				{
    				case 0:					
    					info->fi = data >> 4;
    					info->di = data & 0x0F;
                        info->fd = data;
    					slot->status &= ~EMV_STATE_MODE_SPECIFIC;
    					break;
    				case 1:					
    					if ( data & 0x10 )
    					{
    						info->emv_flag = 0;	/* TA2.b5=1 forbidden */
    					}
                        else
    					{
    						slot->status |= EMV_STATE_MODE_SPECIFIC;
							if ( info->protocol != ( data & 0x0F ) )
							{
								info->emv_flag = 0;
							}
    					}
    					break;
    				case 2:					
    					if ( info->protocol == EMV_PROTOCOL_T1 )
    					{
    						info->ifsc = data;
    					}
    					break;
				}
				if ( Yi & 0x20 )
				{
					state = STATE_PARSE_TB;
				}
				else if ( Yi & 0x40 )
				{
					state = STATE_PARSE_TC;
				}
				else if ( Yi & 0x80 )
				{
					state = STATE_PARSE_TD;
				}
				else
				{
					state = STATE_PARSE_HIST_BYTES;
				}
				break;
			case STATE_PARSE_TB :
				switch( index )
				{
				case 0:			/* TB1 */
					info->p = data & 0x1F;
					break ;
				case 1:			/* TB2 */
					info->emv_flag = 0;
					break ;
				case 2:			/* TB3 */
					info->bwi = data >> 4;
					info->cwi = data & 0x0F;
					break;
				}
	 			if ( Yi & 0x40 )
	 			{
					state = STATE_PARSE_TC;
	 			}
				else if ( Yi & 0x80 )
				{
					state = STATE_PARSE_TD;
				}
				else
				{
					state = STATE_PARSE_HIST_BYTES;
				}
				break;
			case STATE_PARSE_TC :
				switch( index )
				{
					case 0:			/* TC1 */
						info->n = data;
						break;
					case 1:			/* TC2 */
						info->wi = data;
						if ( !data )
						{
							info->emv_flag = 0;
						}
						break ;
					case 2:			/* TC3 */
						if ( data )
						{
							info->emv_flag = 0;
						}
						break ;
				}
	 			if ( Yi & 0x80 )
	 			{
					state = STATE_PARSE_TD;
	 			}
				else
				{
					state = STATE_PARSE_HIST_BYTES;
				}
				break ;
			case STATE_PARSE_TD :
				Yi = data ;
				switch( index++ )
				{
    				case 0:					
    					info->protocol = Yi & 0x0F;
    					if ( info->protocol > EMV_PROTOCOL_T1 )
    					{
    						info->emv_flag = 0;
    					}
    					break;
    				case 1:
						/* Bug correction #769 */
   						if ( ( info->protocol == EMV_PROTOCOL_T0 ) &&
							( ( Yi & 0x0F ) != EMV_PROTOCOL_T1 ) &&
							( ( Yi & 0x0F ) != EMV_PROTOCOL_T14 ) )
						{
							info->emv_flag = 0;
						}
						/*  */
    					else if ( ( info->protocol == EMV_PROTOCOL_T1 ) &&
							( ( Yi & 0x0F ) != EMV_PROTOCOL_T1 ) )
						{
							info->emv_flag = 0;
						}
    					break;
				}
	
				if ( Yi & 0xF0 )
				{
					if ( Yi & 0x10 )
					{
						/* TAx character present */
						state = STATE_PARSE_TA;
					}
					else if ( Yi & 0x20 )
					{
						/* TBx character present */
						state = STATE_PARSE_TB;
					}
					else if ( Yi & 0x40 )
					{
						/* TCx character present */
						state = STATE_PARSE_TC;
					}
					else if ( Yi & 0x80 )
					{
						/* TDx character present */
						state = STATE_PARSE_TD;
					}
					else
					{
						state = STATE_PARSE_HIST_BYTES;
					}
				}
				else
				{
					state = STATE_PARSE_HIST_BYTES;
				}
				break ;
			case STATE_PARSE_HIST_BYTES:
				if ( K )	/* if null, go to test lrc */
				{
					if ( info->k < K )
					{
						info->hist_bytes[info->k++] = data ;
						if ( info->k == K )
						{
							if ( info->protocol > 0 )
							{
								state = STATE_PARSE_TCK;
							}
							else
							{
								slot->error = NO_ERROR ;
								ptr = atr + length;
							}
						}
					}
					break ;
				}
			case STATE_PARSE_TCK:
				if ( !TCK )
				{
					slot->error = NO_ERROR;
				}
				ptr = atr + length;
				break ;
		}

		if ( ( state == STATE_PARSE_HIST_BYTES ) &&
			!K &&
			!info->protocol )
		{
			slot->error = NO_ERROR;
			break ;
		}
	}

	if ( ( slot->status & EMV_STATE_COLD_RESET ) && info->p )
	{
		info->emv_flag = 0;
	}
	slot->status &= ~EMV_STATE_IFSD_PENDING;
    /* Protocol T=1 */
	if ( info->protocol == EMV_PROTOCOL_T1 )
	{
		if ( ( info->bwi > 4 ) || ( info->cwi > 5 ) )
		{
			info->emv_flag = 0;
		}
		if ( ( info->ifsc < 0x10 ) || ( info->ifsc == 0xFF ) )
		{
			info->emv_flag = 0;
		}
		else
		{
			slot->status |= EMV_STATE_IFSD_PENDING;
		}
		if ( info->n == 0xFF )
		{
			guard_time = 0;
		}
		else
		{
			guard_time = info->n + 1;
		}
		if ( (1 << info->cwi) <= guard_time )
		{
			info->emv_flag = 0;
		}
		/* Init indexes for Emission/Reception */
		slot->seq = 0 ;
		slot->flags = 0 ;
		slot->ifsc = info->ifsc ;
	}

	if ( info->fd == 0xD6 )
	{
		info->emv_flag = 0 ;
	}
	return slot->error;
}

void emv_calculate_timing( emv_slot_t *slot, emv_atr_t* info )
{
	if ( slot->protocol == EMV_PROTOCOL_T1 )
	{
	/* MasterCard :
	 *
	 *	BWT = 2^BWI*960*D +11
	 *	WTX = m* BWT
	 *
	 *    - accepted at BWT +D*960
	 *    - rejected at BWT +D*(960+480)
	 *
	 *	Si WTX request 
	 *	  - accepted at  m * ( BWT + D*960)
	 *    - rejected at m * ( BWT + D*(960+480) ) */
	/* 357120 = 960 * 372  */

		slot->bwt = (1 << info->bwi) * 357120;
		slot->bwt /= slot->usFacteurF ;
		slot->bwt *= slot->ucFacteurD ;
		slot->bwt += 11 ;
		/* Bug fix #1139 */
		/* BWT Offset is set in HAL_SCS routines */
		slot->bwt += (slot->ucFacteurD * 960);
		/*  */
	/* MasterCard : Acceptation a CWT+2, rejet a CWT+4 -> limite est fixee a 3 */
		slot->cwt = CWT_OFFSET_ERROR;
		slot->cwt += 11;
		slot->cwt += (1 << info->cwi);
	}
	else
	{
		slot->bwt = 600;
		slot->bwt += 960 * info->wi;
		slot->bwt *= slot->ucFacteurD;
		slot->cwt = slot->bwt;
	}
	return;
}

unsigned char emv_set_FD_values( emv_slot_t *slot, emv_atr_t* info, unsigned char Applicate )
{
	if ( Applicate & HAL_SCS_APPLY_FD )
	{
		slot->ucFacteurD = emv_Di_tab[info->di];
		slot->usFacteurF = emv_Fi_tab[info->fi];
		return info->fd;
	}
	else
	{
		slot->ucFacteurD = emv_Di_tab[DI_DEFAUT];
		slot->usFacteurF = emv_Fi_tab[FI_DEFAUT];
		return 0x11;
	}
}
